package com.ikingtech.framework.sdk.pay.embedded.supplier.wechat;

import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.enums.pay.CashierSupplierEnum;
import com.ikingtech.framework.sdk.pay.embedded.supplier.*;
import com.ikingtech.framework.sdk.utils.Tools;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author tie yan
 */
@Slf4j
public class WechatCashier extends AbstractCashier {

    private static final Map<String, RSAAutoCertificateConfig> MERCHANT_CONFIG = new ConcurrentHashMap<>();

    @Override
    public PayOrder pay(PayArgs args) {
        return switch (args.getPayType()) {
            case WECHAT_MINI, WECHAT_JS -> WechatJsapiPayCashier.prepay(args, this.config, this.getRsaConfig());
            case WECHAT_SCAN -> WechatNativePayCashier.prepay(args, this.config, this.getRsaConfig());
            case WECHAT_APP -> WechatAppPayCashier.prepay(args, this.config, this.getRsaConfig());
            case WECHAT_H5 -> WechatH5PayCashier.prepay(args, this.config, this.getRsaConfig());
            default -> throw new FrameworkException("不支持的支付类型");
        };
    }

    @Override
    public CancelResult cancel(CancelArgs args) {
        switch (args.getPayType()) {
            case WECHAT_MINI:
            case WECHAT_JS:
                WechatJsapiPayCashier.closeOrder(args.getRequestId(), this.config, this.getRsaConfig());
                break;
            case WECHAT_SCAN:
                WechatNativePayCashier.closeOrder(args.getRequestId(), this.config, this.getRsaConfig());
                break;
            case WECHAT_APP:
                WechatAppPayCashier.closeOrder(args.getRequestId(), this.config, this.getRsaConfig());
                break;
            case WECHAT_H5:
                WechatH5PayCashier.closeOrder(args.getRequestId(), this.config, this.getRsaConfig());
                break;
            default:
                throw new FrameworkException("不支持的支付类型");
        }
        return new CancelResult();
    }

    @Override
    public RefundResult refund(RefundArgs args) {
        RefundService refundService = new RefundService.Builder().config(this.getRsaConfig()).build();
        CreateRequest request = new CreateRequest();
        AmountReq refundAmount = new AmountReq();
        refundAmount.setRefund((long) (args.getRefundAmount() * 100));
        refundAmount.setTotal((long) (args.getTotalAmount() * 100));
        refundAmount.setCurrency("CNY");
        request.setAmount(refundAmount);
        request.setOutTradeNo(args.getRequestId());
        request.setOutRefundNo(Tools.Id.uuid());
        Refund refund = refundService.create(request);
        return new RefundResult(Status.SUCCESS.equals(refund.getStatus()), refund.getStatus().name());
    }

    @Override
    public RedPackResult redPack(RedPackArgs args) {
        WechatRedPackParam param = new WechatRedPackParam();
        param.setNonceStr(Tools.Id.uuid());
        param.setMchBillNo(args.getRequestId());
        param.setMchId(this.config.getCustomId());
        param.setWxAppId(this.config.getAppId());
        param.setSendName(this.config.getCustomName());
        param.setReOpenid(args.getChannelUserId());
        param.setTotalAmount((int) (args.getAmount() * 100));
        param.setTotalNum(args.getReceiverCount());
        param.setWishing(args.getWishingWords());
        param.setActName(args.getTitle());
        param.setRemark(Tools.Str.isBlank(args.getRemark()) ? args.getWishingWords() : args.getRemark());
        param.setSign(Tools.Http.md5Sign(Tools.Http.toQueryStr(Tools.Json.objToMap(param)), "&key=", this.config.getExtraApiKey()));
//        String resultStr = Tools.Http.sslPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb", Tools.Json.toXml(param), this.config.getPrivateKey(), this.config.getCustomId());
        String resultStr = Tools.Str.EMPTY;
        RedPackResult result = new RedPackResult();
        WechatRedPackResult redPackResult = Tools.Json.xmlToBean(resultStr, WechatRedPackResult.class);
        if (null == redPackResult) {
            result.setSuccess(false);
            result.setCause(Tools.Str.format("[PAY][微信支付]发放红包响应为空[{}]", resultStr));
            return result;
        }
        if (redPackResult.fail()) {
            result.setSuccess(false);
            result.setCause(Tools.Str.format("[PAY][微信支付]发放红包失败[{}|{}]", redPackResult.getReturnMsg(), redPackResult.getErrorCodeDesc()));
            return result;
        }
        result.setSupplierTradeId(redPackResult.getSendListId());
        result.setRequestId(redPackResult.getMchBillNo());
        result.setSuccess(true);
        result.setSending(true);
        return result;
    }

    @Override
    public RedPackResult readPackConfirm(RedPackArgs args) {
        WechatRedPackParam param = new WechatRedPackParam();
        param.setNonceStr(Tools.Id.uuid());
        param.setMchBillNo(args.getRequestId());
        param.setMchId(this.config.getCustomId());
        param.setAppId(this.config.getAppId());
        param.setBillType("MCHT");
        param.setSign(Tools.Http.md5Sign(Tools.Http.toQueryStr(Tools.Json.objToMap(param)), "&key=", this.config.getExtraApiKey()));
//        String resultStr = Tools.Http.sslPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo", Tools.Json.toXml(param), this.config.getPrivateKey(), this.config.getCustomId());
        String resultStr = Tools.Str.EMPTY;
        RedPackResult result = new RedPackResult();
        WechatRedPackResult redPackResult = Tools.Json.xmlToBean(resultStr, WechatRedPackResult.class);
        if (null == redPackResult) {
            result.setSuccess(false);
            result.setSending(false);
            result.setCause(Tools.Str.format("[PAY][微信支付]查询红包发放结果为空[{}]", resultStr));
            return result;
        }
        if (redPackResult.fail()) {
            result.setSuccess(false);
            result.setSending(false);
            result.setCause(Tools.Str.format("[PAY][微信支付]查询红包发放结果失败[{}|{}]", redPackResult.getReturnMsg(), redPackResult.getErrorCodeDesc()));
            return result;
        }
        result.setSupplierTradeId(redPackResult.getSendListId());
        result.setChannelTradeId(redPackResult.getSendListId());
        result.setRequestId(redPackResult.getMchBillNo());
        result.setSuccess("SENDING".equals(redPackResult.getStatus()) || "SENT".equals(redPackResult.getStatus()) || "RECEIVED".equals(redPackResult.getStatus()));
        result.setSending("SENDING".equals(redPackResult.getStatus()) || "SENT".equals(redPackResult.getStatus()));
        return result;
    }

    @Override
    public PayResult validate(String resultParam, Map<String, Object> resultParamMap) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null == requestAttributes) {
            throw new FrameworkException("交易结果验签失败[微信支付][[http request attributes is null]]");
        }
        HttpServletRequest httpRequest = requestAttributes.getRequest();
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(httpRequest.getHeader("Wechatpay-Serial"))
                .nonce(httpRequest.getHeader("Wechatpay-Nonce"))
                .signature(httpRequest.getHeader("Wechatpay-Signature"))
                .timestamp(httpRequest.getHeader("Wechatpay-Timestamp"))
                .body(resultParam)
                .build();
        NotificationParser parser = new NotificationParser(this.getRsaConfig());
        Transaction transactionResult = parser.parse(requestParam, Transaction.class);
        PayResult result = new PayResult();
        result.setRequestId(transactionResult.getOutTradeNo());
        result.setSupplierTradeId(transactionResult.getTransactionId());
        result.setSuccess(Transaction.TradeStateEnum.SUCCESS.equals(transactionResult.getTradeState()));
        result.setCause(transactionResult.getTradeStateDesc());
        result.setSupplierResponse(Tools.Str.EMPTY);
        return result;
    }

    @Override
    public CashierSupplierEnum supplier() {
        return CashierSupplierEnum.WECHAT_PAY;
    }

    @Override
    public PayResult getPayResult(GetPayResultArgs args) {
        throw new FrameworkException("暂不支持");
    }

    public RSAAutoCertificateConfig getRsaConfig() {
        if (MERCHANT_CONFIG.containsKey(this.config.getCustomId())) {
            return MERCHANT_CONFIG.get(this.config.getCustomId());
        }
        RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(this.config.getCustomId())
                .privateKey(this.config.getPrivateKey())
                .merchantSerialNumber(this.config.getCustomSerialNo())
                .apiV3Key(this.config.getApiKey())
                .build();
        MERCHANT_CONFIG.put(this.config.getCustomId(), config);
        return config;
    }
}
